ES6新增了Class的語法,這個語法糖讓我們用更簡化的程式碼寫出原型鏈。但要注意,雖然我們寫Class語法,但跟之前寫建構函式一樣,背後原理一樣都是操作物件的prototype,得出的結果也是一樣,這一點是沒變的。縱使我們寫Class,JavaScript仍然是一個Prototype-based語言,並非一個完全的物件導向語言。
用Class代替建構函式的好處:
.call、Object.create()去做繼承constructor屬性,我們需要手動補回來,但用Class語法就不用補回class的方法裏面,方法就寫在最外層。避免開發者不經意把屬性、變數等資料,一併共用給下層從最簡單的例子說起,以前會先建立建構函式,並在它的原型裏定義想共享給下層的方法,最後再new出一個實體物件。
function Car(type,color,person){
this.type = type;
this.color = color;
this.person = person;
}
//在建構函式Car的原型物件裏,定義會共享的方法
Car.prototype.carHorn = function(){
console.log(`${this.type}發出車輛的嗚響` )
}
const myCar = new Car('電動車','紅色',4);
console.log(myCar) //Car {type: "電動車", color: "紅色", person: 4}
myCar.carHorn(); //電動車發出車輛的嗚響
但class的寫法就更加乾淨,而且更易閱讀:
class Car{
constructor(type,color,person){
this.type = type;
this.color = color;
this.person = person;
}
carHorn(){
console.log(`${this.type}發出車輛的嗚響`)
}
}
const myCar = new Car('電動車','紅色',4);
以上Class寫法的結構:
類別{
constructor(prop1,prop2,prop3){
建構函式的屬值
}
放在建構函式的原型物件裏的方法
}
extends和super就搞定以前要達成一個下層建構函式繼承上層建構函式的原型物件,我們要:
Object.create繼承上層建構函式的原型物件.call方法,呼叫上層的建構函式,才會吃到上層所定義的屬性constructor會被上層的constructor蓋掉,但正確做法應該是下層的constructor指回自己的建構函式,所以我們要把下層的constructor屬性改回來。function Transportation(transportType){
this.objectType = '交通工具';
this.transportType = transportType;
}
Transportation.prototype.drive = function(){
console.log('我可以駕駛' + this.type)
}
//2. 呼叫上層的建構函式
function Car(type,color,person){
Transportation.call(this,'車輛')
this.type = type;
this.color = color;
this.person = person;
}
//1. Car的原型物件繼承Transportation的原型物件
Car.prototype = Object.create(Transportation.prototype)
Car.prototype.carHorn = function(){
console.log(`${this.type}發出車輛的嗚響` )
}
//3. 把constructor重新指回Car建構函式本身
Car.prototype.constructor = Car
const myCar = new Car('電動車','紅色',4);
但Class語法使整個過程變得更簡短,我們用super和extends就能搞定:
class Transportation{
constructor(transportType){
this.objectType = '交通工具';
this.transportType = transportType;
}
drive(){
console.log('我可以駕駛' + this.type)
}
}
//1. Car繼承Transportation
class Car extends Transportation{
constructor(type,color,person){
//2. 呼叫上層建構函式
super(type)
this.type = type;
this.color = color;
this.person = person;
//不能在this後宣告,否則報錯
// super(type)
}
carHorn(){
console.log(`${this.type}發出車輛的嗚響`)
}
}
const myCar = new Car('電動車','紅色',4);
console.log(myCar); //Car {type: "電動車", color: "紅色", person: 4}
myCar.carHorn(); //電動車發出車輛的嗚響
myCar.drive(); //我可以駕駛電動車
在使用super時有兩點要注意:
extends後要連帶使用super()。這樣才可以在new出一個Car時所傳入的参數,送到Transportation這一層,否則Transportation不知道哪個参數才要被指定成transportType資料。super()要在使用this之前。那麼第3步,Car的constructor呢?我們用console.dir(Car)去查看:

發現Car的constructor並不會因為Car繼承了Transportation而被蓋掉,它仍然是指向Car本身!
以下再驗證比對一下:
console.log(Car.prototype.constructor === Car) //true
但是,其實我們有些情況下是不一定要用super()方法。
如果Car只是用來新增方法,裏面並沒有屬性,這個情況可以不寫super()方法,因為new Car()括號中的参數,一定是傳送到Transportation裏:
class Transportation{
constructor(type){
this.type = type
}
drive(){
console.log('我可以駕駛' + this.type)
}
}
class Car extends Transportation{
carHorn(){
console.log(`${this.type}發出車輛的嗚響`)
}
}
const myCar = new Car('電動車');
console.log(myCar) //Car {type: "電動車"}
myCar.carHorn(); //電動車發出車輛的嗚響
透過加上static在方法前面,我們可以把方法變成靜態方法,讓我們可以直接呼叫這個方法。但是,由這個類別產生出來的實體物件則不能呼叫靜態方法:
class Car{
constructor(type){
this.type = type
}
static carHorn(type){
return `我可以駕駛${type}`
}
}
console.log(Car.carHorn('電動車')) //我可以駕駛電動車
Class的語法糖使我們能夠更快捷建立原型鏈,而且讓程式碼更易閱讀。要注意一點是,其實Class和建構函式是在做同一件事,結果都是一樣的。
JavaScript | ES6 中最容易誤會的語法糖 Class - 基本用法
JS 原力覺醒 Day23 - Class 語法糖
你懂 JavaScript 嗎?#21 ES6 Class
原型基礎物件導向